#include <sdk.h>
#include <wx/filefn.h>
#include <wx/textfile.h>
#include <wx/regex.h>
#include <wx/xml/xml.h>
#include "compilerXML.h"

CompilerXML::CompilerXML( const wxString& name, const wxString& ID, const wxString& file )
  : Compiler( wxGetTranslation( name ), ID ), m_fileName( file ) {
  wxXmlDocument compiler;
  compiler.Load( m_fileName );
  m_Weight = wxAtoi( compiler.GetRoot()->GetAttribute( _T( "weight" ), _T( "100" ) ) );
  m_MultiLineMessages = _T( "0" ) != compiler.GetRoot()->GetAttribute( _T( "multilinemessages" ), _T( "0" ) );
  Reset();
}

CompilerXML::~CompilerXML() {
}

Compiler* CompilerXML::CreateCopy() {
  return ( new CompilerXML( *this ) );
}

AutoDetectResult CompilerXML::AutoDetectInstallationDir() {
  wxXmlDocument compiler;
  compiler.Load( m_fileName );
  wxXmlNode* node = compiler.GetRoot()->GetChildren();
  int depth = 0;
  SearchMode sm = none;
  wxString path;
  wxGetEnv( _T( "PATH" ), &path );
  wxString origPath = path;
  if( !m_MasterPath.IsEmpty() ) {
    path += wxPATH_SEP + m_MasterPath;
    wxSetEnv( _T( "PATH" ), path );
    m_MasterPath.Clear();
  }
  while( node ) {
    if( node->GetName() == _T( "if" ) && node->GetChildren() ) {
      if( EvalXMLCondition( node ) ) {
        node = node->GetChildren();
        ++depth;
        continue;
      } else if( node->GetNext() && node->GetNext()->GetName() == _T( "else" ) &&
                 node->GetNext()->GetChildren() ) {
        node = node->GetNext()->GetChildren();
        ++depth;
        continue;
      }
    } else if( node->GetName() == _T( "Path" ) && node->GetChildren() ) {
      wxString value = node->GetAttribute( _T( "type" ), wxEmptyString );
      if( value == _T( "master" ) ) {
        sm = master;
      } else if( value == _T( "extra" ) ) {
        sm = extra;
      } else if( value == _T( "include" ) ) {
        sm = include;
      } else if( value == _T( "resource" ) ) {
        sm = resource;
      } else if( value == _T( "lib" ) ) {
        sm = lib;
      }
      if( sm != master || m_MasterPath.IsEmpty() ) {
        node = node->GetChildren();
        ++depth;
        continue;
      } else
      { sm = none; }
    } else if( node->GetName() == _T( "Search" ) && sm != none ) {
      wxString value;
      if( node->GetAttribute( _T( "envVar" ), &value ) ) {
        wxString pathValues;
        wxGetEnv( value, &pathValues );
        if( !pathValues.IsEmpty() ) {
          wxArrayString pathArray = GetArrayFromString( pathValues, wxPATH_SEP );
          wxString targ = GetExecName( node->GetAttribute( _T( "for" ), wxEmptyString ) );
          for( size_t i = 0; i < pathArray.GetCount(); ++i ) {
            if( ( targ.IsEmpty() && wxDirExists( pathArray[i] ) ) || wxFileExists( pathArray[i] + _T( '/' ) + targ ) ) {
              if( AddPath( pathArray[i], sm, wxAtoi( compiler.GetRoot()->GetAttribute( _T( "rmDirs" ), _T( "0" ) ) ) ) )
              { break; }
            } else if( sm == master && ( ( targ.IsEmpty() && wxDirExists( value + _T( "/bin" ) ) )
                                         || wxFileExists( pathArray[i] + _T( "/bin/" ) + targ ) ) ) {
              if( AddPath( pathArray[i] + _T( "/bin" ), sm ) ) {
                break;
              }
            }
          }
        }
      } else if( node->GetAttribute( _T( "path" ), &value ) ) {
        wxString targ = GetExecName( node->GetAttribute( _T( "for" ), wxEmptyString ) );
        if( wxIsWild( value ) ) {
          path = wxFindFirstFile( value, wxDIR );
          if( !path.IsEmpty() &&
              ( ( targ.IsEmpty() && wxDirExists( path ) ) ||
                wxFileExists( path + _T( '/' ) + targ ) ||
                wxFileExists( path  + _T( "/bin/" ) + targ ) ) ) {
            AddPath( path, sm );
          }
        } else if( ( targ.IsEmpty() && wxDirExists( value ) ) || wxFileExists( value + _T( '/' ) + targ ) ) {
          AddPath( value, sm );
        } else if( sm == master && ( ( targ.IsEmpty() && wxDirExists( value  + _T( "/bin" ) ) ) || wxFileExists( value  + _T( "/bin/" )  + targ ) ) ) {
          AddPath( value  + _T( "/bin" ), sm );
        }
      } else if( node->GetAttribute( _T( "file" ), &value ) ) {
        wxString regexp = node->GetAttribute( _T( "regex" ), wxEmptyString );
        int idx = wxAtoi( node->GetAttribute( _T( "index" ), _T( "0" ) ) );
        wxRegEx re;
        if( wxFileExists( value ) && re.Compile( regexp ) ) {
          wxTextFile file( value );
          for( size_t i = 0; i < file.GetLineCount(); ++i ) {
            if( re.Matches( file.GetLine( i ) ) ) {
              AddPath( re.GetMatch( file.GetLine( i ), idx ), sm );
              if( sm == master && !m_MasterPath.IsEmpty() )
              { break; }
            }
          }
        }
      }
    } else if( node->GetName() == _T( "Add" ) ) {
      wxString value;
      if( node->GetAttribute( _T( "cFlag" ), &value ) ) {
        AddCompilerOption( value );
      } else if( node->GetAttribute( _T( "lFlag" ), &value ) ) {
        AddLinkerOption( value );
      } else if( node->GetAttribute( _T( "lib" ), &value ) ) {
        AddLinkLib( value );
      } else if( sm != none ) {
        path.Clear();
        wxXmlNode* child = node->GetChildren();
        while( child ) {
          if( child->GetType() == wxXML_TEXT_NODE || child->GetType() == wxXML_CDATA_SECTION_NODE ) {
            path << child->GetContent();
          } else if( child->GetName() == _T( "master" ) ) {
            path << m_MasterPath;
          } else if( child->GetName() == _T( "separator" ) ) {
            path << wxFILE_SEP_PATH;
          } else if( child->GetName() == _T( "envVar" ) ) {
            value = child->GetAttribute( _T( "default" ), wxEmptyString );
            wxGetEnv( child->GetAttribute( _T( "value" ), wxEmptyString ), &value );
            path << value;
          }
          child = child->GetNext();
        }
        AddPath( path.Trim().Trim( false ), sm );
      }
    } else if( node->GetName() == _T( "Fallback" ) && sm != none ) {
      wxString value = node->GetAttribute( _T( "path" ), wxEmptyString );
      switch( sm ) {
        case master:
          if( m_MasterPath.IsEmpty() ) {
            AddPath( value, sm );
          }
          break;
        case extra:
          if( m_ExtraPaths.IsEmpty() ) {
            AddPath( value, sm );
          }
          break;
        case include:
          if( m_IncludeDirs.IsEmpty() ) {
            AddPath( value, sm );
          }
          break;
        case resource:
          if( m_ResIncludeDirs.IsEmpty() ) {
            AddPath( value, sm );
          }
          break;
        case lib:
          if( m_LibDirs.IsEmpty() ) {
            AddPath( value, sm );
          }
          break;
        case none:
        default:
          break;
      }
    }
    while( ( !node->GetNext() || ( sm == master && !m_MasterPath.IsEmpty() ) ) && depth > 0 ) {
      node = node->GetParent();
      if( node->GetName() == _T( "Path" ) ) {
        sm = none;
      }
      --depth;
    }
    node = node->GetNext();
  }
  wxSetEnv( _T( "PATH" ), origPath );
  if( wxFileExists( m_MasterPath + _T( "/bin/" ) + m_Programs.C )
      || wxFileExists( m_MasterPath + _T( '/' ) + m_Programs.C )
      || ( GetID() == _T( "null" ) ) ) {
    return adrDetected;
  }
  for( size_t i = 0; i < m_ExtraPaths.GetCount(); ++i ) {
    if( wxFileExists( m_ExtraPaths[i] + _T( '/' ) + m_Programs.C ) ) {
      return adrDetected;
    }
  }
  return adrGuessed;
}

bool CompilerXML::AddPath( const wxString& pth, SearchMode sm, int rmDirs ) {
  wxFileName fn( pth + _T( '/' ) );
  fn.Normalize( wxPATH_NORM_ENV_VARS | wxPATH_NORM_DOTS );
  for( int i = rmDirs; i > 0; --i ) {
    fn.RemoveLastDir();
  }
  wxString path = fn.GetPath();
  switch( sm ) {
    case master:
      if( path.AfterLast( _T( '/' ) ) == _T( "bin" ) ) {
        m_MasterPath = path.BeforeLast( _T( '/' ) );
      } else
      { m_MasterPath = path; }
      return true;
    case extra:
      if( m_ExtraPaths.Index( path, true ) == wxNOT_FOUND ) {
        m_ExtraPaths.Add( path );
      }
      break;
    case include:
      AddIncludeDir( path );
      break;
    case lib:
      AddLibDir( path );
      break;
    case none:
    default:
      break;
  }
  return false;
}
